Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 모집공고 작성 임시저장 기능 구현 #166

Merged
merged 181 commits into from
May 7, 2024

Conversation

hyosin-Jang
Copy link
Collaborator

@hyosin-Jang hyosin-Jang commented Apr 20, 2024

closes #165

💡 다음 이슈를 해결했어요.

임시저장 기능 구현

💡 이슈를 처리하면서 추가된 코드가 있어요.

1. 임시저장하기 버튼 클릭한 경우

2. 마이페이지 > 임시저장 목록

  • 마이페이지의 임시저장 목록을 보시면 스터디 탭과 모집공고 탭으로 나뉘어져 있습니다.
  • 선택된 카드에 따라 localStorage에서 해당 카드를 포함하는 리스트만 추출해서 뿌려줬습니다.
  • parseTimestampFromUUID 함수로 timestamp를 구한 후, 최신순으로 정렬했습니다.
  • uuid는 현재 시간과 랜덤한 MAC 주소로 생성되는 v1을 사용했습니다. (각 버전별 uuid 참고)
  const parseTimestampFromUUID = (uuid: string) => {
    // uuid의 3번째 부분이 timestamp, 시간 보정을 위해 값을 빼줌
    return new Date(parseInt(uuid.split('-')[2], 16) - 12219292800000).getTime();
  };

  const getTempList = (selectedCard: 'STUDY' | 'RECRUITMENT') => {
    const savedList: Array<Partial<RecruitmentForm> & { savedKey: string }> = [];
    for (const key in window.localStorage) {
      // hasOwnProperty로 빌트인 속성 제거
      if (window.localStorage.hasOwnProperty(key) && key.toUpperCase().includes(selectedCard)) {
        savedList.push({ ...JSON.parse(localStorage.getItem(key)), savedKey: key });
      }
    }
    savedList.sort((a, b) => parseTimestampFromUUID(b.savedKey) - parseTimestampFromUUID(a.savedKey));
    return savedList;
  };

3. 임시저장 글 이어서 작성하기

  • 카드를 클릭하면 이어서 작성할 수 있고 페이지가 전환됩니다. 이때, 선택한 카드의 값을 받아오기 위해 savedKey를 store에 저장했습니다.
export interface SavedKeyState {
  savedKey: string;
  setSavedKey: (newKey: string) => void;
}

// 선택된 카드 키
export const useSavedKeyStore = create<SavedKeyState>((set) => ({
  savedKey: '',
  setSavedKey: (newKey: string) => set(() => ({ savedKey: newKey })),
}));
  • 스터디 모집공고 페이지에서는 임시저장된 키가 있는 경우, parseSelectValue 함수를 통해 초기값을 <Select> 또는 <Input>에 들어가는 형태로 가공합니다.
  • api 초기값으로 세팅하는 경우, type: 'api', data: {apiResponse로 받은 데이터}를 전달합니다.
  • storage에서 가져온 값으로 세팅하는 경우, type: 'storage'를 사용합니다.
  • ex. applicationCount: 3 -> { label: '3명', value: 3 }
 import { RecruitFormWithSelect } from '@/Types/study';
import { APPLICATION_CNT, CONTACT } from '@/Shared/study';
import { useSavedKeyStore } from '@/store/study';

const defValue = { label: '', value: '' };
export const useSelectDefaultValue = (type: 'api' | 'storage', data?: any) => {
  const savedKey = useSavedKeyStore((state) => state.savedKey);
  const tempSaved: RecruitFormWithSelect | null = JSON.parse(localStorage.getItem(savedKey)) ?? null;

  /**
   *
   * @descriptioin 임시저장과 api에 들어온 값을 셀렉트 option에 들어가는 타입으로 가공합니다.
   */
  const parseSelectValue = (formKey: keyof RecruitFormWithSelect) => {
    if (type === 'storage' && !tempSaved) return;
    if (type === 'storage') return tempSaved[formKey];

    switch (formKey) {
      case 'contact':
        return CONTACT?.find((contact) => contact.value === data[formKey]) ?? defValue;
      case 'applicantCount':
        return APPLICATION_CNT?.find((cnt) => cnt.value === data[formKey]) ?? defValue;
      case 'positionIds':
        return data?.positionIds?.map(({ id, name }: { id: number; name: string }) => ({ value: id, label: name }));
      default:
        return data[formKey] ?? defValue;
    }
  };

  return parseSelectValue;
};
  • 임시저장 목록에서 이어서 작성한 후, 스터디 새로 생성 시 값이 채워지는 것 방지하기 위해 언마운트될 때 선택된 키를 초기화해줍니다.
  • 해당 로직이 스터디 공고 페이지에서도 재사용되어서 useTempSaved 훅으로 분리했습니다.
// 임시저장 관련 훅
export const useTempSaved = () => {
  const savedKey = useSavedKeyStore((state) => state.savedKey);
  const setSavedKey = useSavedKeyStore((state) => state.setSavedKey);
  const tempSaved: RecruitFormWithSelect | null = JSON.parse(localStorage.getItem(savedKey)) ?? null;

  useEffect(() => {
    return () => {
      // 언마운트될 때, 임시저장 선택된 키 초기화
      if (tempSaved) setSavedKey('');
    };
  }, []);

  return { tempSaved, savedKey, setSavedKey };
};

4. 이어서 작성한 글을 임시 저장할 경우

이어서 작성한 글을 임시 저장할 경우, uuid를 현재 timestamp로 갱신해야 하기 때문에 key를 새로 생성하고 이전 key를 삭제합니다.

 const saveTemporary = () => {
    // 저장된 키가 있는 경우 이전 키를 삭제한다.
    if (savedKey.length > 0) localStorage.removeItem(savedKey);

    const newSavedKey = `RECRUITMENT-${studyId}-${uuidv1()}`;
    localStorage.setItem(newSavedKey, JSON.stringify(data));
  };

💡 필요한 후속작업이 있어요.

  • 임시저장 목록 uuid 시간순으로 정렬
  • 임시저장 목록 > title 빈 경우 처리
  • 브랜치 전환 시 생성되는 yarn/cache/**/* 파일들, (제가 설정을 잘못한걸 수도 있는데 yarn 3.x를 사용하면서 nodeLinker가 pnp가 아닌 node-modules로 되어 있습니다. zero-install인 경우 yarn/cache도 같이 레포에 올려주는 것으로 알고 있는데 다른 분들은 이 부분 문제 없으신지 궁금합니다.)
  • yarn.lock 충돌
  • DOM 관련 브라우저 warning들 (isOpen,)


✅ 셀프 체크리스트

  • 브랜치 전략에 맞는 브랜치에 PR을 올리고 있습니다. (master/main이 아닙니다.)
  • 커밋 메세지를 컨벤션에 맞추었습니다.
  • 변경 후 코드는 컴파일러/브라우저 warning/error 가 발생시키지 않습니다.
  • 변경 후 코드는 기존의 테스트를 통과합니다.
  • 테스트 추가가 필요한지 검토해보았고, 필요한 경우 테스트를 추가했습니다.
  • docs 수정이 필요한지 검토해보았고, 필요한 경우 docs를 수정했습니다.

hyosin-Jang and others added 30 commits April 10, 2024 22:03
src/Pages/CreateRecruitment/page.tsx Outdated Show resolved Hide resolved
@hyosin-Jang hyosin-Jang requested a review from abiriadev May 2, 2024 08:18
… 변경 (isOwner -> $isOwner) (SP-445)"

This reverts commit 9c472b2.
Copy link
Member

@abiriadev abiriadev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RHF를 굉장히 자세하게 조사해보신 점 멋집니다.

수고하셨습니다.

@hyosin-Jang hyosin-Jang merged commit c94e65d into dev May 7, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

임시저장 기능 구현
3 participants